#include "tellenc_priv.h"			/* this also includes tellenc.h */

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "my_debug.h"

#define TELLENC_SF_INITED			0x1

static enum tellenc_utf8_state
						tellenc_utf8_statemap[TELLENC_BYTE_MAX];
static int				tellenc_flags = 0;

static void				tellenc_utf8_statemap_init(void);

#ifdef TELLENC_COUNT_BYTE
static int				tellenc_bc_count_cmp(const void *_lhs0,
											 const void *_rhs0);
#endif

static int				tellenc_dbc_count_cmp(const void *_lhs0,
											  const void *_rhs0);
static int				tellenc_dbc_code_cmp(
							const struct tellenc_dbyte_count *lhs,
							const struct tellenc_dbyte_count *rhs);

static struct tellenc_ctx *
						tellenc_ctx_create(const void *_buf, int _len);
void					tellenc_ctx_destroy(struct tellenc_ctx *_ctx);

static enum tellenc_enc	tellenc_check_ucs_bom(struct tellenc_ctx *_ctx);
static enum tellenc_enc	tellenc_lookup_dbyte_enc(int _dbyte);

static int				tellenc_increase_char_count(struct tellenc_ctx *_ctx,
													int _ch);
static int				tellenc_scan(struct tellenc_ctx *_ctx);

static enum tellenc_enc	tellenc_most_freq_dbyte_enc(struct tellenc_ctx *_ctx);
static enum tellenc_enc	tellenc_analyze(struct tellenc_ctx *_ctx);

static void				tellenc_print_analysis(struct tellenc_ctx *_ctx);

#ifndef __unused
#define __unused __attribute__((unused))
#endif

RB_PROTOTYPE_STATIC(tellenc_dbc_tree, tellenc_dbyte_count,
					tedc_node, tellenc_dbc_code_cmp);

RB_GENERATE_STATIC(tellenc_dbc_tree, tellenc_dbyte_count,
				   tedc_node, tellenc_dbc_code_cmp);

int
tellenc_init(void)
{
	if (tellenc_flags & TELLENC_SF_INITED)
		return 0;

	tellenc_utf8_statemap_init();

	tellenc_flags |= TELLENC_SF_INITED;

	return 0;
}

void
tellenc_done(void)
{
	/* EMPTY */
}

enum tellenc_enc
tellenc_tell(const char *buf, int len, int *bom_len, int flags)
{
	enum tellenc_enc enc = TELLENC_ENC_MIN;
	struct tellenc_ctx *ctx;

	/* Actually re-run tellenc_init() is okay, it only sets some static
	   data to constant values, thus we do not bother to employ some
	   synchronizing facilitiy (like some mutex). Avoiding something like
	   "#include <pthread.h>" makes this program more easily to compile, even
	   on platforms like Windoz (at least I hope so...) */
	if (!(tellenc_flags & TELLENC_SF_INITED)) {
		tellenc_init();
	}

	ctx = tellenc_ctx_create(buf, len);
	if (ctx == NULL)
		return TELLENC_ENC_MIN;

	if (tellenc_scan(ctx) != 0) {
		LOGERROR("%s tellenc_scan failed\n", __func__);
		goto back;
	}

	enc = tellenc_analyze(ctx);

	if (ctx->te_flags & TELLENC_F_UCSBOM) {
		if (ctx->te_bomenc == TELLENC_UTF_8 && enc == TELLENC_ASCII) {
				/* Special case, ASCII text with UTF-8 BOM */
				enc = TELLENC_ASCII;
		} else if (ctx->te_bomenc != enc) {
			LOGDEBUG_LV(1, "%s UCS BOM is %s, but scan result is %s\n",
						__func__, tellenc_enc_name(ctx->te_bomenc),
						tellenc_enc_name(enc));
			/* XXX: honor BOM? */
			enc = ctx->te_bomenc;
		}
	}

	if (bom_len != NULL) {
		*bom_len = ctx->te_bomlen;
	}

	if (flags & TELLENC_FLAG_VERBOSE) {
		tellenc_print_analysis(ctx);
	}

back:
	tellenc_ctx_destroy(ctx);
	return enc;
}

const char *
tellenc_enc_name(enum tellenc_enc enc)
{
#define TELLENC_ENC_NAME_TAG(e)		[TELLENC_##e] = #e
	static const char *const tellenc_enc_name_map[] = {
		TELLENC_ENC_NAME_TAG(UNKNOWN),
		TELLENC_ENC_NAME_TAG(BINARY),
		TELLENC_ENC_NAME_TAG(ASCII),
		TELLENC_ENC_NAME_TAG(LATIN1),
		TELLENC_ENC_NAME_TAG(WINDOWS_1252),
		TELLENC_ENC_NAME_TAG(CP437),
		TELLENC_ENC_NAME_TAG(GB2312),
		TELLENC_ENC_NAME_TAG(GBK),
		TELLENC_ENC_NAME_TAG(BIG5),
		TELLENC_ENC_NAME_TAG(SJIS),
		TELLENC_ENC_NAME_TAG(UCS_4BE),
		TELLENC_ENC_NAME_TAG(UCS_4LE),
		TELLENC_ENC_NAME_TAG(UTF_16BE),
		TELLENC_ENC_NAME_TAG(UTF_16LE),
		TELLENC_ENC_NAME_TAG(UTF_8)
	};

	if (enc > TELLENC_ENC_MIN && enc < TELLENC_ENC_MAX)
		return tellenc_enc_name_map[enc];

	LOGDEBUG_LV(3, "%s invalid enc %d\n", __func__, enc);
	return "<invalid>";
}

static void
tellenc_utf8_statemap_init(void)
{
	static int inited = 0;
	int ch = 0;

	if (inited)
		return;

	/* initialize UTF-8 code state table */

	tellenc_utf8_statemap[ch] = TELLENC_UTF8_INVAL;
	++ch;

    for (; ch <= 0x7f; ++ch) {
        tellenc_utf8_statemap[ch] = TELLENC_UTF8_1;
    }

    for (; ch <= 0xbf; ++ch) {
        tellenc_utf8_statemap[ch] = TELLENC_UTF8_TAIL;
    }

    for (; ch <= 0xc1; ++ch) {
        tellenc_utf8_statemap[ch] = TELLENC_UTF8_INVAL;
    }

    for (; ch <= 0xdf; ++ch) {
        tellenc_utf8_statemap[ch] = TELLENC_UTF8_2;
    }

    for (; ch <= 0xef; ++ch) {
        tellenc_utf8_statemap[ch] = TELLENC_UTF8_3;
    }

    for (; ch <= 0xf4; ++ch) {
        tellenc_utf8_statemap[ch] = TELLENC_UTF8_4;
    }

    for (; ch <= 0xff; ++ch) {
        tellenc_utf8_statemap[ch] = TELLENC_UTF8_INVAL;
    }

	inited = 1;
}

#ifdef TELLENC_COUNT_BYTE
/* Compare function for `qsort', sort on .tebc_count of struct
   tellenc_byte_count, reverse-sorting */
static int
tellenc_bc_count_cmp(const void *lhs0, const void *rhs0)
{
	const struct tellenc_byte_count *lhs = lhs0;
	const struct tellenc_byte_count *rhs = rhs0;

	if (lhs->tebc_count > rhs->tebc_count)
		return -1;
	if (lhs->tebc_count < rhs->tebc_count)
		return 1;
	return 0;
}
#endif

/* Compare function for `qsort', sort on .tedc_count of struct
   tellenc_dbyte_count, compare for struct pointer, reverse-sorting */
static int
tellenc_dbc_count_cmp(const void *lhs0, const void *rhs0)
{
	const struct tellenc_dbyte_count *const *lhs = lhs0;
	const struct tellenc_dbyte_count *const *rhs = rhs0;

	if ((*lhs)->tedc_count > (*rhs)->tedc_count)
		return -1;
	if ((*lhs)->tedc_count < (*rhs)->tedc_count)
		return 1;
	return 0;
}

/* Compare function for the red-black tree, sort on .tedc_code */
static int
tellenc_dbc_code_cmp(const struct tellenc_dbyte_count *lhs,
					 const struct tellenc_dbyte_count *rhs)
{
	if (lhs->tedc_code > rhs->tedc_code)
		return 1;
	if (lhs->tedc_code < rhs->tedc_code)
		return -1;
	return 0;
}

static struct tellenc_ctx *
tellenc_ctx_create(const void *buf, int len)
{
#ifdef TELLENC_COUNT_BYTE
	int i;
#endif
	struct tellenc_ctx *ctx;

	ctx = calloc(1, sizeof(*ctx));
	if (ctx == NULL) {
		LOGERROR("%s calloc(1, %u) failed\n", __func__, sizeof(*ctx));
		return NULL;
	}

	ctx->te_buf = buf;
	ctx->te_buflen = len;

#ifdef TELLENC_COUNT_BYTE
	/* initialize byte count table */
	for (i = 0; i < (int)ARRAY_SIZE(ctx->te_byte_tbl); ++i) {
		ctx->te_byte_tbl[i].tebc_code = i;
	}
#endif

	/* initialize red-black tree for double-byte counting */
	RB_INIT(&ctx->te_tree);

	return ctx;
}

void
tellenc_ctx_destroy(struct tellenc_ctx *ctx)
{
	int i;
	struct tellenc_dbyte_count *p, *q;

	if (ctx == NULL) {
		LOGDEBUG_LV(1, "%s null ctx\n", __func__);
		return;
	}

	if (ctx->te_dbyte_tbl != NULL) {
		for (i = 0; i < ctx->te_dbuniq_cnt; ++i) {
			free(ctx->te_dbyte_tbl[i]);
		}
		free(ctx->te_dbyte_tbl);
	} else if (!RB_EMPTY(&ctx->te_tree)) {
		RB_FOREACH_SAFE(p, tellenc_dbc_tree, &ctx->te_tree, q) {
			RB_REMOVE(tellenc_dbc_tree, &ctx->te_tree, p);
			free(p);
		}
	}

	free(ctx);
}

static enum tellenc_enc
tellenc_check_ucs_bom(struct tellenc_ctx *ctx)
{
    static const struct {
		enum tellenc_enc			bom_enc;
		const char					*bom_pat;
		int							bom_len;
    } bom_pat[] = {
		/* The following entrys' order MATTERS! */
        { TELLENC_UCS_4BE,			"\x00\x00\xFE\xFF",			4 },
        { TELLENC_UCS_4LE,			"\xFF\xFE\x00\x00",			4 },
        { TELLENC_UTF_8,			"\xEF\xBB\xBF",				3 },
        { TELLENC_UTF_16BE,			"\xFE\xFF",					2 },
        { TELLENC_UTF_16LE,			"\xFF\xFE",					2 },
    };

	int i;

    for (i = 0; i < (int)ARRAY_SIZE(bom_pat); ++i) {
		if (ctx->te_buflen < bom_pat[i].bom_len)
			continue;
		if (memcmp(ctx->te_buf, bom_pat[i].bom_pat,
					bom_pat[i].bom_len) == 0) {
			ctx->te_bomenc = bom_pat[i].bom_enc;
			ctx->te_bomlen = bom_pat[i].bom_len;
			return 0;
		}
    }

	return -1;
}

static enum tellenc_enc
tellenc_lookup_dbyte_enc(int dbyte)
{
	int i;

	for (i = 0; i < (int)ARRAY_SIZE(tellenc_freqmap); ++i) {
		if (dbyte == tellenc_freqmap[i].tem_code) {
			return tellenc_freqmap[i].tem_enc;
		}
	}

	return TELLENC_ENC_MIN;
}

static int
tellenc_increase_char_count(struct tellenc_ctx *ctx, int ch)
{
	struct tellenc_dbyte_count dbc, *p, *elm;

#ifdef MY_DEBUG
	if (ch < 0x100) {
		LOGDEBUG_LV(1, "%s ch (%d) is ASCII?\n", __func__, ch);
	}
#endif

	dbc.tedc_code = ch;
	p = RB_FIND(tellenc_dbc_tree, &ctx->te_tree, &dbc);
	if (p != NULL) {
		/* character already occured before, just increase the count */
		++p->tedc_count;
		return 0;
	}

	/* the first occurence of the character */
	//LOGDEBUG_LV(5, "%s insert node for char 0x%04X\n", __func__, ch);
	elm = calloc(1, sizeof(*elm));
	if (elm == NULL) {
		LOGERROR("%s calloc(1, %u) failed for struct tellenc_dbyte_count\n",
				 __func__, sizeof(*elm));
		return -1;
	}
	elm->tedc_code = ch;
	elm->tedc_count = 1;

	p = RB_INSERT(tellenc_dbc_tree, &ctx->te_tree, elm);
	if (p != NULL) {
		/* What the hell? */
		LOGERROR("%s node with code 0x%04X already exists?\n",
				 __func__, ch);
		++p->tedc_count;
		free(elm);
	} else {
		//LOGDEBUG_LV(5, "%s insert node with char 0x%04X success\n", __func__, ch);
	}

	return 0;
}

static int
tellenc_scan(struct tellenc_ctx *ctx)
{
	int i, rc, ch, buflen, last_ch = EOF;
	const unsigned char *buf;
	enum tellenc_utf8_state st = TELLENC_UTF8_1;

	if (ctx->te_buflen <= 0) {
		LOGDEBUG_LV(1, "%s buflen (%d) is 0?\n", __func__, ctx->te_buflen);
		ctx->te_flags |= TELLENC_F_EMPTY;
		return -1; /* XXX: */
	}

	rc = tellenc_check_ucs_bom(ctx);
	if (rc == 0) {
		ctx->te_flags |= TELLENC_F_UCSBOM;
		/* XXX: continue to scan */
	}

	buf = ctx->te_buf + ctx->te_bomlen;
	buflen = ctx->te_buflen - ctx->te_bomlen;

	for (i = 0; i < buflen; ++i) {
		ch = buf[i];

#ifdef TELLENC_COUNT_BYTE
		/* record byte occurences */
		++(ctx->te_byte_tbl[ch].tebc_count);
#endif

		if (TELLENC_CHAR_IS_NON_TEXT(ch)) {
			/* check for binary data (including UTF-16/32) */
			if (!(ctx->te_flags & TELLENC_F_BINARY) &&
					!(ch == TELLENC_DOS_EOF && i == buflen - 1)) {
				ctx->te_flags |= TELLENC_F_BINARY;
			}

			if (ch == TELLENC_NUL) {
				/* null values at byte EVEN and ODD */
				++(ctx->te_nul_byte[i & 1]);

				if ((i & 1) && ctx->te_buf[i - 1] == TELLENC_NUL) {
					/* null values at word EVEN and ODD */
					++(ctx->te_nul_word[(i / 2) & 1]);
				}
			}
		}

		/* validate latin1 */
		if (!(ctx->te_flags & TELLENC_F_NON_LATIN1)) {
			if (ch >= 0x80 && ch < 0xa0) {
				ctx->te_flags |= TELLENC_F_NON_LATIN1;
			}
		}

		/* validate UTF-8 */
		if (!(ctx->te_flags & TELLENC_F_NON_UTF8)) {
			switch (tellenc_utf8_statemap[ch]) {
            case TELLENC_UTF8_TAIL:
                if (st > TELLENC_UTF8_1) {
                    --st;
                } else {
					ctx->te_flags |= TELLENC_F_NON_UTF8;
                }
                break;

			case TELLENC_UTF8_1:
				if (st != TELLENC_UTF8_1) {
					ctx->te_flags |= TELLENC_F_NON_UTF8;
				}
				break;

			case TELLENC_UTF8_2:
				if (st != TELLENC_UTF8_1) {
					ctx->te_flags |= TELLENC_F_NON_UTF8;
				} else {
					st = TELLENC_UTF8_2;
				}
				break;

			case TELLENC_UTF8_3:
				if (st != TELLENC_UTF8_1) {
					ctx->te_flags |= TELLENC_F_NON_UTF8;
				} else {
					st = TELLENC_UTF8_3;
				}
				break;

			case TELLENC_UTF8_4:
				if (st != TELLENC_UTF8_1) {
					ctx->te_flags |= TELLENC_F_NON_UTF8;
				} else {
					st = TELLENC_UTF8_4;
				}
				break;

			case TELLENC_UTF8_INVAL:
				/* FALL THROUGH */
			default:
				ctx->te_flags |= TELLENC_F_NON_UTF8;
				break;
			}
		} /* if (!(ctx->te_flags & TELLENC_F_NON_UTF8)) */

		/* construct double-byte and count it */
		if (last_ch != EOF) {
			int dbch = (last_ch << 8) | ch;

			tellenc_increase_char_count(ctx, dbch);
			++ctx->te_dbyte_cnt;
			if (last_ch > 0xa0 && ch > 0xa0) {
				++ctx->te_dbhihi_cnt;
			}

			last_ch = EOF;
		} else if (ch >= 0x80) {
			last_ch = ch;
		}
	} /* for (i = 0; i < buflen; ++i) */

	return 0;
}

/*
 * Get the encoding of most frequently occured double byte character
 */
static enum tellenc_enc
tellenc_most_freq_dbyte_enc(struct tellenc_ctx *ctx)
{
#define TELLENC_FREQ_CHECK_MAX		10
	struct tellenc_dbyte_count *p, **arr;
	enum tellenc_enc enc;
	int i;

	if (ctx->te_dbyte_cnt <= 0) {
		LOGERROR("%s count of all double-byte characters is 0?\n", __func__);
		return TELLENC_ENC_MIN;
	}

	if (ctx->te_dbyte_tbl != NULL) {
		/* XXX: Could this happen? */
		LOGERROR("%s ctx->te_dbyte_tbl not NULL?\n", __func__);
		free(ctx->te_dbyte_tbl);
		ctx->te_dbyte_tbl = NULL;
	}

	/* Count tree nodes, i.e., the number of unique double-byte characters */
	i = 0;
	RB_FOREACH(p, tellenc_dbc_tree, &ctx->te_tree) {
		++i;
	}
	if (i == 0) {
		LOGERROR("%s double-byte chars tree counts zero!\n", __func__);
		return TELLENC_ENC_MIN;
	}
	ctx->te_dbuniq_cnt = i;

	arr = calloc(ctx->te_dbuniq_cnt, sizeof(*arr));
	if (arr == NULL) {
		LOGERROR("%s calloc(%d, %u) for dbyte array failed\n",
				__func__, ctx->te_dbuniq_cnt, sizeof(*arr));
		return TELLENC_ENC_MIN;
	}
	/* Store the allocated array and free it when destroying the ctx */
	ctx->te_dbyte_tbl = arr;

	i = 0;
	RB_FOREACH(p, tellenc_dbc_tree, &ctx->te_tree) {
		/* DAMN, the following can happen?
		if (i >= ctx->te_dbuniq_cnt) {
			LOGERROR("%s i %d, double-byte unique count %d, why?\n",
					 __func__, i, ctx->te_dbuniq_cnt);
			return TELLENC_ENC_MIN;
		}
		*/
		arr[i] = p;
		++i;
	}

	/* sort the double byte char count, in descending order */
	qsort(arr, ctx->te_dbuniq_cnt, sizeof(*arr), tellenc_dbc_count_cmp);

	for (i = 0; i < TELLENC_FREQ_CHECK_MAX && i < ctx->te_dbuniq_cnt; ++i) {
		enc = tellenc_lookup_dbyte_enc(arr[i]->tedc_code);
		if (enc != TELLENC_ENC_MIN) {
			return enc;
		}
	}

	return TELLENC_ENC_MIN;
}

static enum tellenc_enc
tellenc_analyze(struct tellenc_ctx *ctx)
{
	enum tellenc_enc enc;

	/* Heuristics for UTF-16/32 */
	if ((ctx->te_flags & TELLENC_F_NON_UTF8) &&
			(ctx->te_flags & TELLENC_F_BINARY)) {
		if (ctx->te_nul_byte[TELLENC_EVEN] > 4 &&
				(ctx->te_nul_byte[TELLENC_ODD] == 0 ||
				 (ctx->te_nul_byte[TELLENC_EVEN] /
				  ctx->te_nul_byte[TELLENC_ODD] > 20))) {
			return TELLENC_UTF_16BE;
		} else if (ctx->te_nul_byte[TELLENC_ODD] > 4 &&
				(ctx->te_nul_byte[TELLENC_EVEN] == 0 ||
				 (ctx->te_nul_byte[TELLENC_ODD] /
				  ctx->te_nul_byte[TELLENC_EVEN] > 20))) {
			return TELLENC_UTF_16LE;
		} else if (ctx->te_nul_word[TELLENC_EVEN] > 4 &&
				(ctx->te_nul_word[TELLENC_ODD] == 0 ||
				 (ctx->te_nul_word[TELLENC_EVEN] /
				  ctx->te_nul_word[TELLENC_ODD] > 20))) {
			return TELLENC_UCS_4BE;
		} else if (ctx->te_nul_word[TELLENC_ODD] > 4 &&
				(ctx->te_nul_word[TELLENC_EVEN] == 0 ||
				 (ctx->te_nul_word[TELLENC_ODD] /
				  ctx->te_nul_word[TELLENC_EVEN] > 20))) {
			return TELLENC_UCS_4LE;
		}
		return TELLENC_BINARY;
	}

	if (ctx->te_dbyte_cnt == 0) {
		/* No characters outside the scope of ASCII */
		return TELLENC_ASCII;
	}

	if (!(ctx->te_flags & TELLENC_F_NON_UTF8)) {
		/* Only valid UTF-8 sequences */
		return TELLENC_UTF_8;
	}

	enc = tellenc_most_freq_dbyte_enc(ctx);
	if (enc != TELLENC_ENC_MIN) {
		if (enc == TELLENC_GBK &&
				ctx->te_dbhihi_cnt == ctx->te_dbyte_cnt) {
			/* Special case for GB2312: no high-byte followed by a low byte */
			return TELLENC_GB2312;
		}
		return enc;
	}

	if (ctx->te_dbhihi_cnt * 100 / ctx->te_dbyte_cnt < 5) {
		/* Mostly a low-byte follows a high-byte */
		if (ctx->te_flags & TELLENC_F_NON_LATIN1) {
			return TELLENC_WINDOWS_1252;
		} else {
			/* LATIN1 is subset of WINDOW-1252 */
			return TELLENC_LATIN1;
		}
	}

	/* We are too stupid to guess the encoding */
	return TELLENC_UNKNOWN;
}

static void
tellenc_print_analysis(struct tellenc_ctx *ctx)
{
	int i;
#ifdef TELLENC_COUNT_BYTE
	int ch;


	/* sort the byte count table, in descending order */
	qsort(ctx->te_byte_tbl, TELLENC_BYTE_MAX,
			sizeof(ctx->te_byte_tbl[0]), tellenc_bc_count_cmp);

	for (i = 0; i < TELLENC_BYTE_MAX; ++i) {
		if (ctx->te_byte_tbl[i].tebc_count == 0)
			break;
		ch = ctx->te_byte_tbl[i].tebc_code;
		LOGINFO_R("%3d (0x%02X, '%c'): %-6d", ch, ch,
				  (isprint(ch) ? ch : '.'), ctx->te_byte_tbl[i].tebc_count);
		if ((i + 1) % 5 == 0)
			LOGINFO_R("\n");
	}
	if (i % 5 != 0)
		LOGINFO_R("\n");
#endif

	/* print double-byte characters counts */
	for (i = 0; i < ctx->te_dbuniq_cnt; ++i) {
		LOGINFO_R("0x%04X: %-6d", ctx->te_dbyte_tbl[i]->tedc_code,
				  ctx->te_dbyte_tbl[i]->tedc_count);
		if ((i + 1) % 8 == 0)
			LOGINFO_R("\n");
	}
	if (i % 8 != 0)
		LOGINFO_R("\n");

	LOGINFO_R("buffer %p, %d bytes (BOM %d bytes)\n"
			  "double-byte        chars: %d\n"
			  "hi-hi double-byte  chars: %d\n"
			  "unique double-byte chars: %d\n"
			  "null byte         [0, 1]: [%d, %d]\n"
			  "null word         [0, 1]: [%d, %d]\n"
			  "flags: BINARY %d, UTF8 %d, LATIN1 %d, UCS BOM %d, EMPTY %d\n",
			  ctx->te_buf, ctx->te_buflen, ctx->te_bomlen,
			  ctx->te_dbyte_cnt, ctx->te_dbhihi_cnt, ctx->te_dbuniq_cnt,
			  ctx->te_nul_byte[TELLENC_EVEN], ctx->te_nul_byte[TELLENC_ODD],
			  ctx->te_nul_word[TELLENC_EVEN], ctx->te_nul_word[TELLENC_ODD],
			  !!(ctx->te_flags & TELLENC_F_BINARY),
			  !(ctx->te_flags & TELLENC_F_NON_UTF8),
			  !(ctx->te_flags & TELLENC_F_NON_LATIN1),
			  !!(ctx->te_flags & TELLENC_F_UCSBOM),
			  !!(ctx->te_flags & TELLENC_F_EMPTY)
			  );

	if(ctx->te_flags & TELLENC_F_UCSBOM) {
		LOGINFO_R("UCS BOM encoding: %s\n", tellenc_enc_name(ctx->te_bomenc));
	}
}
